#include <iostream>
#include "Sock.hpp"
#include<sys/select.h>
#include<string>

#define NUM (sizeof(fd_set) * 8)

int fd_array[NUM]; //内容>=0,合法的fd，如果是-1,该位置没有fd

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cout << "Usage: \n\t" << argv[0] << " port" << std::endl; 
        exit(1);
    }
    uint16_t port = (uint16_t)atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);
    for(int i=0; i<NUM; i++)
    {
        fd_array[i] = -1;//初始化，数组里面没有fd
    }
    // 不应该accept: accept的本质叫做通过listen_sock获取新链接，前提是listen_sock上面有新链接
    // accept不知道有新链接！所以accept阻塞式等待
    // 站在多路转接的视角，我们认为链接到来，对于listen_sock,就是读事件就绪
    // 对于所有的服务器，最开始的时候，只有listen_sock
    //事件循环
    fd_set rfds;
    fd_array[0] = listen_sock;
    for(;;)
    {
        FD_ZERO(&rfds);
        int max_fd = fd_array[0];
        for(int i=0; i<NUM; i++)
        {
            if(fd_array[i] == -1) continue;
            //下面的都是合法的fd
            FD_SET(fd_array[i], &rfds);//所有关心读事件的fd，添加到rfds中
            
            max_fd = max(max_fd, fd_array[i]);//更新最大fd
        }

        struct timeval timeout = {0,0};
        // 服务器上的所有的fd(包括listen_sock),都要交给select进行检测
        // recv,read,write,send,accept : 只负责自己最核心的工作：真正的读写(listen_sock:accept)
        int n = select(max_fd+1, &rfds, nullptr, nullptr, nullptr);
        switch (n)
        {
        case -1:
            std::cerr << "select error" << std::endl;
            break;
        case 0:
            std::cout << "select timeout" << std::endl;
            break;
        default:
            cout << "有fd对应的事件就绪啦!" << std::endl;
            for(int i=0; i<NUM; i++)
            {
                if(fd_array[i] == -1)//非法fd，跳过
                    continue;
                //fd合法，开始判断是否就绪。如果没有就绪 -》 不处理
                if(FD_ISSET(fd_array[i], &rfds))//就绪
                {
                    std::cout << "sock: " << fd_array[i] << " 上面有了读事件，可以读取了" << std::endl;
                    //分为listen套接字就绪 和 普通套接字就绪
                    if(fd_array[i] == listen_sock)
                    {
                        std::cout << "listen_sock: " << listen_sock << " 有了新的链接到来" << std::endl;
                        //accept
                        int sock = Sock::Accept(listen_sock);
                        if(sock >= 0)
                        {
                            std::cout << "listen_sock: " << listen_sock << " 获取新的链接成功" << std::endl;
                            // 获取成功
                            // 新链接到来，不意味着有数据到来。我们要将这个新的fd放进数组里，让他也加入select的监听
                            int pos = 1;
                            for(; pos<NUM; pos++)//去找一个空位给sock
                            {
                                if(fd_array[pos] == -1)
                                {
                                    break;
                                }
                            }
                            if(pos < NUM)
                            {
                                std::cout << "新链接: " << sock << " 已经被添加到了数组[" << pos << "]的位置" << std::endl;
                                fd_array[pos] = sock;
                            }
                            else//说明服务器满载，要关闭这个新链接
                            {
                                std::cout << "服务器已经满载了，关闭新的链接" << std::endl;
                                close(sock);
                            }
                        }
                    }
                    else//普通的套接字，直接进行读取逻辑
                    {
                        std::cout << "sock: " << fd_array[i] << " 上面有普通读取" << std::endl;

                        char recv_buffer[1024] = {0};
                        ssize_t s = recv(fd_array[i], recv_buffer, sizeof(recv_buffer)-1, 0);
                        if(s > 0)
                        {
                            recv_buffer[s] = 0;
                            std::cout << "client["<<fd_array[i]<<"]# " << recv_buffer << std::endl; 
                        }
                        else if (s == 0)
                        {
                            std::cout << "sock: " << fd_array[i] << "关闭了, client退出啦!" << std::endl;
                            //对端关闭链接,我们这边也要关掉对应的套接字
                            close(fd_array[i]);
                            std::cout << "已经在数组下标fd_array[" << i << "]"
                                      << "中,去掉了sock: " << fd_array[i] << std::endl;
                            fd_array[i] = -1;//该位置置为-1
                        }
                        else
                        {
                            //读取失败
                            close(fd_array[i]);
                            std::cout << "已经在数组下标fd_array[" << i << "]"
                                      << "中,去掉了sock: " << fd_array[i] << std::endl;
                            fd_array[i] = -1;
                        }
                    }
                }
            }
            break;
        }
    }
    return 0;
}
